package com.allin.http.request;

import android.text.TextUtils;
import android.util.Log;

import com.allin.http.ParamUtil;
import com.allin.http.callback.ResultCallback;
import com.google.gson.GsonBuilder;
import com.squareup.okhttp.FormEncodingBuilder;
import com.squareup.okhttp.MediaType;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.RequestBody;

import java.io.File;
import java.io.IOException;
import java.util.Map;

import okio.BufferedSink;

/**
 * POST 方式提交
 * Created by zhy on 15/11/6.
 */
public class OkHttpPostRequest extends OkHttpRequest
{
    private String content;  //需要提交的文本内容
    private byte[] bytes;    //需要提交的字节流
    private File file;        //需要提交的文件
    private MediaType mediaType;  //客户端>服务器 内容传输格式
    private int bytesLength; //字节长度

    //参数类型-与内容传输格式一 一对应
    private int type=0;   //默认x-www-form-urlencoded    key=value&key2=value2
    private static final int TYPE_PARAMS = 1;  //默认json    {key:value,key2:value2}
    private static final int TYPE_STRING = 2;
    private static final int TYPE_BYTES = 3;
    private static final int TYPE_FILE = 4;
    private static final int TYPE_BYTES_STRING = 5; //字节的方式字符串提交

    public static final int CHUNK_SIZE = 1*1024; //每次提交的字节数,TYPE_BYTES_STRING时启用
    private final MediaType MEDIA_TYPE_STREAM = MediaType.parse("application/octet-stream;charset=utf-8");
    private final MediaType MEDIA_TYPE_STRING = MediaType.parse("text/plain;charset=utf-8");
    private final MediaType MEDIA_TYPE_JSON = MediaType.parse("application/json;charset=utf-8");

    //定义几种默认的内容传输格式
    protected OkHttpPostRequest(String url, Object tag, Map<String, Object> params, Map<String, String> headers, MediaType mediaType, String content, byte[] bytes, File file)
    {
        super(url, tag, params, headers);
        this.mediaType = mediaType;
        this.content = content;
        this.bytes = bytes;
        this.bytesLength=this.bytes!=null?this.bytes.length:0;
        this.file = file;
    }

    protected void validParams()
    {
        int count = 0;

        if (content != null)
        {
            type = TYPE_STRING;
            count++;
        }
        if (bytes != null && this.bytesLength>0 && params != null) {//map方式字节流写入
            type = TYPE_BYTES_STRING;
            count++;
        }else if (params != null && !params.isEmpty()){
            type = TYPE_PARAMS;
            count++;
        }else if (bytes != null){
            type = TYPE_BYTES;
            count++;
        }
    if (file != null)
        {
            type = TYPE_FILE;
            count++;
        }

        if (count <= 0 || count > 1)
        {
            throw new IllegalArgumentException("the params , content , file , bytes must has one and only one .");
        }
    }

    /**
     * 构建带表单的请求
     * @return
     */
    @Override
    protected Request buildRequest()
    {
        if (TextUtils.isEmpty(url))
        {
            throw new IllegalArgumentException("url can not be empty!");
        }
        Request.Builder builder = new Request.Builder();
        appendHeaders(builder, headers);
        builder.url(url).tag(tag).post(requestBody);
        return builder.build();
    }

    /**
     * 构建参数体
     * map类型的参数采用json的方式向服务器发送数据，
     * 如果不指定参数传递类型，默认采用url encode的方式向服务器发送数据
     * @return
     */
    @Override
    protected RequestBody buildRequestBody()
    {
        validParams();
        RequestBody requestBody = null;
        switch (type)
        {
            case TYPE_PARAMS:
                String jsonParam = new GsonBuilder().create().toJson(params);
                requestBody=RequestBody.create(mediaType != null ? mediaType : MEDIA_TYPE_JSON, jsonParam);
                break;
            case TYPE_BYTES_STRING:
                requestBody=buildBytesStringBody();
                break;
            case TYPE_BYTES:
                requestBody = RequestBody.create(mediaType != null ? mediaType : MEDIA_TYPE_STREAM, bytes);
                break;
            case TYPE_FILE:
                requestBody = RequestBody.create(mediaType != null ? mediaType : MEDIA_TYPE_STREAM, file);
                break;
            case TYPE_STRING:
                requestBody = RequestBody.create(mediaType != null ? mediaType : MEDIA_TYPE_STRING, content);
                break;
            default:
                FormEncodingBuilder builder = new FormEncodingBuilder();
                ParamUtil.addParams(builder, params);
                requestBody = builder.build();
                break;
        }
        return requestBody;
    }


    @Override
    protected RequestBody wrapRequestBody(RequestBody requestBody, final ResultCallback callback)
    {
        CountingRequestBody countingRequestBody = new CountingRequestBody(requestBody, new CountingRequestBody.Listener()
        {
            @Override
            public void onRequestProgress(final long bytesWritten, final long contentLength)
            {

                mOkHttpClientManager.getDelivery().post(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        callback.inProgress(bytesWritten * 1.0f / contentLength);
                    }
                });

            }
        });
        return countingRequestBody;
    }

    protected RequestBody buildBytesStringBody()
    {
        requestBody = new RequestBody() {
            @Override public MediaType contentType() {
                return MEDIA_TYPE_JSON;
            }

            @Override public void writeTo(BufferedSink sink) throws IOException {
                int offset=0;
                while(offset<bytesLength){
                    int chunkSize=calcPutSize(offset);
                    sink.write(bytes,offset,chunkSize);
                    offset+=chunkSize;
                }
            }
            @Override
            public long contentLength(){
                Log.i("process  size:","================"+bytesLength);
                return bytesLength;
            }
        };
        return requestBody;
    }


    private int calcPutSize(int offset) {
//        Log.i("content infocalcPutSize","---------offset:"+offset);
        int left = bytesLength - offset;//待提交的字节位置
        return left < CHUNK_SIZE ? left : CHUNK_SIZE;
    }
}
